/*
 * Copyright © 2009 Gerald Isaac Schwarz
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

package cli;

import java.awt.Point;
import java.io.File;
import java.util.ArrayList;
import java.util.Scanner;

import base.*;

public class CLI {
	public static final Character NEWLINE = Character.LINE_SEPARATOR;
	
	private BoardWidget boardWidget;
	private ArrayList<Set> sets;
	private Engine engine;
	private Client client;
	private ArrayList<String> exitValues = new ArrayList<String>();
	
	private TurnListener turnListener = new TurnListener() {
		public void turnPerformed(TurnEvent te) {
			if (!CLI.this.engine.isCheckmate(CLI.this.engine.getVirtualBoard().getCurrentPlayer())) {
				printBoard();
				showMenu();
			}
			else {
				printBoard();
				System.out.println("Checkmate on player " + CLI.this.engine.getVirtualBoard().getCurrentPlayer().getName() + "!");
			}
		}
	};
	
	private boolean isExitValue(String value) {
		for (String exitValue : this.exitValues) {
			if (value.equals(exitValue)) {
				return true;
			}
		}
		
		return false;
	}
	
	private int getInt(String prompt) {
		InputWidget inputWidget = new InputWidget(prompt);
		int input = inputWidget.promptForInteger();
		this.testInput(input + "");
		return input;
	}
	
	private String getString(String prompt) {
		InputWidget inputWidget = new InputWidget(prompt);
		String input = inputWidget.promptForString();
		this.testInput(input);
		return input;
	}
	
	private boolean isLoadValue(String value) {
		if (value.equals("load")) {
			return true;
		}
		else {
			return false;
		}
	}
	
	private void testInput(String input) {
		if (this.isExitValue(input)) {
			this.exit();
		}
		else if(this.isSaveValue(input)) {
			this.save();
		}
		else if(this.isLoadValue(input)) {
			this.load();
		}
	}
	
	private boolean isSaveValue(String value) {
		if (value.equals("save")) {
			return true;
		}
		else {
			return false;
		}
	}

	private void load() {
		File file = new File(this.getString("What file do you wish to load?"));
		
		if (file.exists()) {
			this.engine = (new GameLoader(file)).getEngine();
			
			this.engine.addTurnListener(this.turnListener);
			this.boardWidget = new BoardWidget(this.engine);
			this.boardWidget.setSet(this.sets.get(0));
			
			LocalServer server = new LocalServer();
			this.client = server.register();
			server.setGame(new Game(this.client, this.engine));
			
			this.printBoard();
			this.showMenu();
		}
		else {
			System.out.println("Specified file doesn't exist.");
			this.load();
		}
	}
	
	private void save() {
		File file = new File(this.getString("Where do you want to save the game?"));
		
		if (!file.exists()) {
			new GameSaver(this.engine, file);
		}
		else {
			System.out.println("Specified file already exists.");
			this.save();
		}
	}
	
	private void exit() {
		String question = "Are you sure you wish to exit the game?", yes = "Yes", no = "No";
		ArrayList<String> options = new ArrayList<String>();
		options.add(yes);
		options.add(no);
		SelectWidget<String> exitMenu = new SelectWidget<String>(question, options);
		String input = exitMenu.prompt();
			
		if (input.equals(yes)) {
			System.exit(0);
		}
		else if (!input.equals(no)) {
			System.out.println("Invalid input");
			this.exit();
		}
	}
	
	private void printBoard() {
		this.getBoardWidget().refreshBoard();
		System.out.println(this.getBoardWidget());
	}
	
	
	private BoardWidget getBoardWidget() {
		return this.boardWidget;
	}

	private void drop() {
		ArrayList<Piece> pieces = this.engine.getVirtualBoard().getCapturedPieces(this.engine.getVirtualBoard().getCurrentPlayer());
		SelectWidget<Piece> selectWidget = new SelectWidget<Piece>("Please Select a piece:", pieces);
		Piece selectedPiece = selectWidget.prompt();
		
		System.out.println("Please Select a destination:");
		int xCoordinate = this.getInt("X: ");
		int yCoordinate = this.getInt("Y: ");
		
		try {
			this.engine.drop(this.client, selectedPiece, new Point(xCoordinate, yCoordinate));
		} catch (InvalidDropException e) {
			System.out.println("Please choose another destination.");
			this.showMenu();
		}
	}
	
	
	private void move() {
		Player player = this.engine.getVirtualBoard().getCurrentPlayer();
		
		System.out.println(player.getName() + ", please select a piece:");
		int xCoordinate = this.getInt("X: ");
		int yCoordinate = this.getInt("Y: ");
		
		Piece piece = this.engine.getVirtualBoard().getPiece(new Point(xCoordinate, yCoordinate));
		
		if (piece != null && this.engine.getVirtualBoard().getCurrentState(piece).getOwner() == player) {
			System.out.println("Please select a destination:");
			xCoordinate = this.getInt("X: ");
			yCoordinate = this.getInt("Y: ");
			
			try {
				Point destination = new Point(xCoordinate, yCoordinate);
				this.engine.move(this.client, piece, destination);
				
				if (this.engine.isUpgradeable(piece) && this.engine.getVirtualBoard().isInUpgradeZone(piece)) {
					this.upgrade();
				}
			} catch (InvalidMoveException e) {
				System.out.println("Please choose another destination.");
				this.showMenu();
			}
		}
		else {
			System.out.println("Invalid input.");
			this.showMenu();
		}
	}
	
	
	private void upgrade() {
		State state = this.engine.getVirtualBoard().getCurrentState(this.engine.getSelectedPiece());
		SelectWidget<String> selectWidget = new SelectWidget<String>("What upgrade would you like to apply to your " + 
				state.getUpgrade().getType() + "?", state.getUpgrade().getUpgradeOptions());
		try {
			String type = selectWidget.prompt();
			this.engine.upgrade(this.client, type);
		}
		catch (InvalidUpgradeException e) {
			System.out.println("Invalid action.");
			upgrade();
		}
	}
	
	private void showMenu() {
		if (this.engine.getVirtualBoard().getCapturedPieces(this.engine.getVirtualBoard().getCurrentPlayer()).size() > 0) {
			String move = "Move", drop = "Drop";
			ArrayList<String> options = new ArrayList<String>();
			options.add(move);
			options.add(drop);
			SelectWidget<String> selectWidget = 
				new SelectWidget<String>(this.engine.getVirtualBoard().getCurrentPlayer().getName() + ", What do you wish to do?", options);
			
			String selectedOption = selectWidget.prompt();
			
			if (selectedOption.equals(move)) {
				this.move();
			}
			else if (selectedOption.equals(drop)) {
				this.drop();
			}
		}
		else {
			this.move();
		}
	}
	
	private CLI() {
		String set = "shogi";
		ArrayList<String> pieces = new ArrayList<String>();
		pieces.add("King");
		pieces.add("Rook");
		pieces.add("Bishop");
		pieces.add("Gold General");
		pieces.add("Silver General");
		pieces.add("Knight");
		pieces.add("Lance");
		pieces.add("Pawn");
		pieces.add("Upgraded Rook");
		pieces.add("Upgraded Bishop");
		
		ArrayList<String> representations = new ArrayList<String>();
		representations.add("K");
		representations.add("R");
		representations.add("B");
		representations.add("G");
		representations.add("S");
		representations.add("N");
		representations.add("L");
		representations.add("P");
		representations.add("O");
		representations.add("I");
		
		this.sets = new ArrayList<Set>();
		this.sets.add(new Set(set, pieces, representations));
		
//		Set exit values:
		
		this.exitValues.add("exit");
		this.exitValues.add("quit");
		
//		Greeting:
		
		System.out.println("Hello, and welcom to sysboard shogi CLI edition. To exit at any time, just enter the word exit. To load a game, enter the word load. To save a game, enter the word save.");
		
		this.load();
	}
	
	public static void main(String[] args) {
		new CLI();
	}
}
